package com.rt.code.generator.freemarker;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.rt.code.bean.A1templatetable;
import com.rt.code.generator.freemarker.dto.GenerateDto;
import com.rt.code.generator.freemarker.exec.FmUtils;
import com.rt.code.generator.freemarker.exec.FreemarkerGen;
import com.rt.code.generator.freemarker.utils.MpFileWriter;
import com.rt.easyjava.base.biz1.mapper.TmptUserSettingMapper;
import com.rt.tmpt.dto.A1TemplateTableDto;
import com.rt.tmpt.dto.Result;
import com.rt.tmpt.utils.DateTool;
import com.rt.tmpt.utils.DateUtils;
import com.rt.tmpt.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

/**
 * @author wuxiaoyong
 * @version 1.0.1
 * mybatis-plus 动态生成器
 */
@Scope("prototype")
@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class MpGeneratorComplex {
    @Resource
    private FreemarkerGen freemarkerGen;
    private A1templatetable templateTableNew;

    @Resource
    private TmptUserSettingMapper tmptUserSettingMapper;

    // @Value("${mybatis-plus.generator.path.vue}")
    private String outputVueDirProj;
    // @Value("${mybatis-plus.generator.path.java}")
    private String outputDirProj;
    // @Value("${mybatis-plus.generator.module}")
    private String module;
    //
    private String domain;

    private String outputDirView;
    private String outputDirRouter;
    private String outputDirMain;
    private String javaDir;
    private String packagePathParent;
    private String packageDotParent;
    private String stripPrefix;

    private String packagePath;
    private String dtoOutputDir;
    private String testOutputDir;
    private String mapperXmlOutputDir;
    private String dozerOutputDir;
    private String pgOutputDir;

    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.url}")
    private String jdbc_url;
    @Value("${spring.datasource.driver}")
    private String jdbc_driver;



    private void initPath(GenerateDto generateDto){
        // 页面参数
        outputDirProj = generateDto.getJavaProjectPath();
        outputVueDirProj = generateDto.getVueProjectPath();
        module = generateDto.getModuleName();
        domain = generateDto.getDomainName();
        // 参数，包路径
        packageDotParent = generateDto.getPackageDotParent();
        if (packageDotParent!=null) {
            packagePathParent = packageDotParent.replaceAll("\\.", "/");
        }
        stripPrefix = generateDto.getStripPrefix();

        if (!generateDto.getVueProjectPath().contains("src")) {
            outputVueDirProj = generateDto.getVueProjectPath()+File.separator+"src";
        }

        freemarkerGen.initTemplateTable(generateDto.getKeyRandom());
        templateTableNew = freemarkerGen.getTemplateTableNew();
        String versionJoStr = templateTableNew.getVersion();
        if (!StringUtils.isNull(versionJoStr)) {
            try {
                JSONObject versionJo = JSONObject.parseObject(versionJoStr);
                module = versionJo.getString("module");
                domain = versionJo.getString("domain");
                stripPrefix = versionJo.getString("stripPrefix");
                // 参数，包路径
                packageDotParent = versionJo.getString("packageDotParent");
                if (packageDotParent!=null) {
                    packagePathParent = packageDotParent.replaceAll("\\.", "/");
                }
                generateDto.setModuleName(module);
                generateDto.setDomainName(domain);
                generateDto.setStripPrefix(stripPrefix);
                generateDto.setPackageDotParent(packageDotParent);
            }catch (Exception e){}
        }

        outputDirMain = outputDirProj+File.separator+module+File.separator+"src"+File.separator+"main"+File.separator;
        outputDirView =  outputVueDirProj+File.separator+"views"+File.separator+"coding"+File.separator+module+File.separator+domain;
        outputDirRouter =  outputVueDirProj+File.separator+"router"+File.separator+"coding";

        javaDir = "java"+File.separator;
        //packagePath = "com/rt/easyjava/"+module+"/modules/"+domain+"/";
        packagePath = packagePathParent+"/"+module+"/modules/"+domain+"/";
        pgOutputDir = outputDirMain + javaDir + packagePath;
        dtoOutputDir = pgOutputDir + "/dto/";
        testOutputDir = pgOutputDir + "/service/test/";
        mapperXmlOutputDir = outputDirMain + "/resources/mapper/"+domain+"/";
        dozerOutputDir = outputDirMain + "/resources/dozer/"+domain+"/";

        /*username = resourceBundle.getString("druid.username");
        password = resourceBundle.getString("druid.password.plain");
        jdbc_url = resourceBundle.getString("druid.url");
        jdbc_driver = resourceBundle.getString("druid.driver");*/
    }



    public void execute(GenerateDto generateDto, HttpServletResponse response) throws IOException {
        initPath(generateDto);

        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(outputDirMain + javaDir);
        gc.setFileOverride(true);
        gc.setActiveRecord(true);// 开启 activeRecord 模式
        gc.setEnableCache(false);// XML 二级缓存
        gc.setBaseResultMap(true);// XML ResultMap
        gc.setBaseColumnList(true);// XML columList
        gc.setDateType(DateType.ONLY_DATE);

        // 此处设置生成人名字
        gc.setAuthor("wuxiaoyong");

        // 自定义文件命名，注意 %s 会自动填充表实体属性！
        gc.setMapperName("%sBaseMapper" );
        gc.setXmlName("%sBaseMapper" );
        gc.setServiceName("%sBaseService" );
        gc.setServiceImplName("%sBaseServiceImpl" );
        gc.setControllerName("%sBaseController" );
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MARIADB);
        dsc.setTypeConvert(new MySqlTypeConvert());
        dsc.setDriverName(jdbc_driver);
        dsc.setUsername(username);
        dsc.setPassword(password);
        dsc.setUrl(jdbc_url);
        mpg.setDataSource(dsc);

        gen(generateDto, gc, mpg, response);
    }

    /**
     *
     */
    private void gen(GenerateDto generateDto, GlobalConfig gc, AutoGenerator mpg, HttpServletResponse response) throws IOException {
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        // strategy.setCapitalMode(true);// 全局大写命名
        // strategy.setDbColumnUnderline(true);//全局下划线命名
        strategy.setTablePrefix(new String[] {stripPrefix});// 此处可以修改为您的表前缀
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
        /*strategy.setInclude(new String[]{"org_charge_bill","org_charge_bill_detail"});*/ // 需要生成的表
        //strategy.setInclude(new String[]{"test_logs_urlprop"});
        // strategy.setExclude(new String[]{"test"}); // 排除生成的表
        // 自定义实体父类
        // strategy.setSuperEntityClass("com.baomidou.demo.TestEntity");
        // 自定义实体，公共字段
        // strategy.setSuperEntityColumns(new String[] { "test_id", "age" });
        // 自定义 mapper 父类
        // strategy.setSuperMapperClass("com.baomidou.demo.TestMapper");
        // 自定义 service 父类
        // strategy.setSuperServiceClass("com.baomidou.demo.TestService");
        // 自定义 service 实现类父类
        // strategy.setSuperServiceImplClass("com.baomidou.demo.TestServiceImpl");
        // 自定义 controller 父类
        //strategy.setSuperControllerClass("com.runtong.agri.web.controller.BaseController" );
        // 【实体】是否生成字段常量（默认 false）
        // public static final String ID = "test_id";
        // strategy.setEntityColumnConstant(true);
        // 【实体】是否为构建者模型（默认 false）
        // public User setName(String name) {this.name = name; return this;}
        // strategy.setEntityBuliderModel(true);
        mpg.setStrategy(strategy);


        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(null);  //所属模块
        // pc.setParent("com.rt.easyjava"+"."+module+".modules."+domain); // 自定义包路径
        pc.setParent(packageDotParent+"."+module+".modules."+domain); // 自定义包路径
        pc.setController("controller.base"); // 这里是控制器包名，默认 web
        pc.setEntity("model.bean.base" );
        pc.setXml("mapper.base" );
        pc.setMapper("repository.mapper.base" );
        pc.setService("service" );
        //pc.setService("service.intf.base" );
        //pc.setServiceImpl("service.impl.base" );
        mpg.setPackageInfo(pc);

        //
        TemplateConfig tc = new TemplateConfig();
        tc.setXml(null);
        tc.setEntity(null);
        tc.setController(null); // 这里是控制器包名，默认 web
        tc.setMapper(null);
        tc.setService(null);
        tc.setServiceImpl(null);
        mpg.setTemplate(tc);
        //
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());

        Map<String, Object> map = freemarkerGen.getMap(module);
        //map.put("package_base_name", "com.rt.easyjava."+module);
        map.put("package_base_name", packageDotParent+"."+module);
        map.put("package_name", pc.getParent());
        map.put("package_utils", "com.rt."+module+".utils");
        map.put("package_mvc_path", "/coding/");
        map.put("package_superMapperClassPackage", "com.rt.utils.base");
        map.put("module", module);
        String tableName = (String)map.get("tableName");
        strategy.setInclude(new String[]{tableName}); // 需要生成的表

        // 注入自定义配置，可以在 VM 中使用 cfg.abc 设置的值
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                this.setMap(map);
            }
        };

        List<FileOutConfig> focList = new ArrayList<>();
        Set<String> outFileFullPathList = new HashSet<>();
        MpFileWriter genFileWriter = new MpFileWriter(focList, outFileFullPathList);

        /*
         genTypeInteger
         All(0, "生成所有"),
         Vue(1, "前端"),
         ServiceAll(2, "后端所有"),
         ServiceTmpt(3, "后端通用模板部分"),
         ServiceMyself(4, "后端自定义部分");
         */
        int genType = 3;
        if (generateDto.getGenType() != null) {
            genType = generateDto.getGenType().intValue();
        }
        switch (genType) {
            case 0: {
                // base
                genTmptBase(genFileWriter);
                // extend
                genTmptExtend(genFileWriter);
                // vue
                genVue(genFileWriter, map, focList, cfg, null);
                break;
            }
            case 1: {
                // vue
                genVue(genFileWriter, map, focList, cfg, null);
                break;
            }
            case 2: {
                // base
                genTmptBase(genFileWriter);
                // extend
                genTmptExtend(genFileWriter);
                break;
            }
            case 3: {
                // base
                genTmptBase(genFileWriter);
                break;
            }
            case 4: {
                // extend
                genTmptExtend(genFileWriter);
                break;
            }
            case 5: {
                genVueRef(genFileWriter, map, focList, cfg);
                break;
            }
        }

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // tmpt log
        String tmptFileFilePath = genTmptFile(freemarkerGen, generateDto);
        outFileFullPathList.add(tmptFileFilePath);

        // 执行生成
        mpg.execute();

        for (String path: outFileFullPathList) {
            System.out.println(path);
        }
        // zip download
        if (generateDto.isDownloadZip()) {
            freemarkerGen.zipSourceFiles(outFileFullPathList, response);
        }
    }

    private String genTmptFile(FreemarkerGen freemarkerGen, GenerateDto generateDto) throws IOException {
        String version = DateTool.getCurrentDateStr1("yyyyMMdd-HHmmss");
        // 更新 version
        generateDto.setModuleName(module);
        Result result = freemarkerGen.getMap4TmptFile4UpdateTmptTable(version, generateDto);
        String tableName = (String) result.getMap().get("tableName");
        String fileName = module+"."+tableName +"."+ version +".txt";
        String tmptPath = outputDirProj+File.separator+/*"core"+File.separator+"src"+File.separator+"main"+File.separator+"resources"+File.separator
                +*/"tmptFile"+File.separator + tableName+File.separator;
        FileWriter fw = null;
        try {
            File filePath = new File(tmptPath);
            if(!filePath.exists()) filePath.mkdirs();
            fw = new FileWriter(tmptPath+fileName);
            fw.write((String) result.getMap().get("tmptJson"));
            return tmptPath+fileName;
        }finally {
            if(fw!=null) {
                fw.close();
            }
        }
    }

    private void genTmptBase(MpFileWriter genFileWriter){
        // base controller
        genFileWriter.write("/templates/generator/base/controller.java.ftl",
                pgOutputDir+File.separator+"controller"+File.separator+"base",  "%sBaseController.java");
        // base mapper.java
        genFileWriter.write("/templates/generator/base/mapper.java.ftl",
                pgOutputDir+File.separator+"repository"+File.separator+"mapper"+File.separator+"base",  "%sBaseMapper.java");
        // base: dto/base
        genFileWriter.write("/templates/generator/base/dto.java.ftl",
                pgOutputDir+File.separator+"dto"+File.separator+"base", "%sBaseDto.java");

        /*// base: service/base
        genFileWriter.write("/templates/generator/base/service.java.ftl",
                pgOutputDir+File.separator+"service"+File.separator+"intf"+File.separator+"base", "%sService.java");
        // base: serviceImpl/base
        genFileWriter.write("/templates/generator/base/serviceImpl.java.ftl",
                pgOutputDir+File.separator+"service"+File.separator+"impl"+File.separator+"base", "%sServiceImpl.java");
        */
        // bean
        genFileWriter.write("/templates/generator/base/entity.java.ftl",
                pgOutputDir+File.separator+"model"+File.separator+"bean"+File.separator+"base", "%sBase.java");
        // base: base/mapper.xml
        genFileWriter.write("/templates/generator/base/mapper.xml.ftl",
                mapperXmlOutputDir+File.separator+"base", "%sBaseMapper.xml");
    }

    private void genTmptExtend(MpFileWriter genFileWriter){
        genFileWriter.write("/templates/generator/biz/controller.java.ftl",
                pgOutputDir+File.separator+"controller", "%sController.java");
        // extend dto
        genFileWriter.write("/templates/generator/biz/dto.java.ftl",
                pgOutputDir+File.separator+"dto", "%sDto.java");
        // extend entity
        genFileWriter.write("/templates/generator/biz/entity.java.ftl",
                pgOutputDir+File.separator+"model"+File.separator+"bean", "%s.java");
        // extend mapper.java
        genFileWriter.write("/templates/generator/biz/mapper.java.ftl",
                pgOutputDir+File.separator+"repository"+File.separator+"mapper", "%sMapper.java");
        // extend repository.java
        genFileWriter.write("/templates/generator/biz/repository.java.ftl",
                pgOutputDir+File.separator+"repository", "%sRepository.java");
        // extend facade.java
        genFileWriter.write("/templates/generator/biz/facade.java.ftl",
                pgOutputDir+File.separator+"facade", "%sFacade.java");
        // extend mapper.xml
        genFileWriter.write("/templates/generator/biz/mapper.xml.ftl",
                mapperXmlOutputDir, "%sMapper.xml");
        // extend service 这个是直接impl了
        genFileWriter.write("/templates/generator/biz/service.java.ftl",
                pgOutputDir+File.separator+"service", "%sService.java");
        // extend serviceImpl
        /*genFileWriter.write("/templates/generator/modules/serviceImpl.java.ftl",
                pgOutputDir+File.separator+"service"+File.separator+"impl", "%sBizServiceImpl.java");*/
        // dozer ，dozer可能会修改，生成base代码时，不要生成dozer了
        genFileWriter.write("/templates/generator/base/dozer.xml.ftl",
                dozerOutputDir, "%s.xml");
    }

    private void genVueRef(MpFileWriter genFileWriter, Map<String, Object> map, List<FileOutConfig> focList, InjectionConfig cfg){
        genVue(genFileWriter, map, focList, cfg, "tmp");
    }

    private void genVue(MpFileWriter genFileWriter, Map<String, Object> map, List<FileOutConfig> focList, InjectionConfig cfg, String appendPath){
        genFileWriter.write("/templates/generator/vue/list.vue.ftl",
                outputDirView, true, "list.vue", appendPath);
        genFileWriter.write("/templates/generator/vue/form-details.vue.ftl",
                outputDirView, true, "form-details.vue", appendPath);
        genFileWriter.write("/templates/generator/vue/form-add.vue.ftl",
                outputDirView, true, "form-add.vue", appendPath);
        genFileWriter.write("/templates/generator/vue/api.js.ftl",
                outputDirView, true, "api.js", appendPath);
        genFileWriter.write("/templates/generator/vue/data.js.ftl",
                outputDirView, true, "data.js", appendPath);
        genFileWriter.write("/templates/generator/vue/index.vue.ftl",
                outputDirView, true, "index.vue", appendPath);
        genFileWriter.write("/templates/generator/vue/list-line-edit.vue.ftl",
                outputDirView, true, "list-line-edit.vue", appendPath);
        genFileWriter.write("/templates/generator/vue/complex-list.vue.ftl",
                outputDirView, true, "complex-list.vue", appendPath);
        genFileWriter.write("/templates/generator/vue/complex-list-line-edit.vue.ftl",
                outputDirView, true, "complex-list-line-edit.vue", appendPath);
        genFileWriter.write("/templates/generator/vue/aside-calendar.vue.ftl",
                outputDirView, true, "aside-calendar.vue", appendPath);


        List<Map> o2mList = (List<Map>)map.get("o2mList");
        for (Map o2mMap: o2mList) {
            Boolean hasO2M = (Boolean) o2mMap.get("hasO2M");
            String classNameO2M = (String) o2mMap.get("classNameO2M");
            if (hasO2M == true) {
                /*genFileWriter.write("/templates/generator/vue/aside-o2m.vue.ftl",
                        outputDirView, true, "aside-o2m-"+classNameO2M+".vue");
                */
                String templatePath = "/templates/generator/vue/aside-o2m.vue.ftl";
                String outputDir = outputDirView;
                boolean hasEntityPath = true;
                String suffixPath = "aside-o2m-"+classNameO2M+".vue";
                focList.add(new FileOutConfig(templatePath) {
                    @Override
                    public String outputFile(TableInfo tableInfo) {
                        cfg.getMap().put("o2m", o2mMap);

                        // vue中最后一个目录是entityName
                        String outputDirTmp = outputDir;
                        if (hasEntityPath) {
                            outputDirTmp = outputDir + File.separator + tableInfo.getEntityPath();
                        }
                        if (!StringUtils.isNull(appendPath)) {
                            outputDirTmp = outputDirTmp  + File.separator + appendPath;
                        }
                        // java中类名和entityName有关
                        String targetFile = suffixPath;
                        if (suffixPath.contains("%s")) {
                            targetFile = tableInfo.getEntityName()+suffixPath.substring(suffixPath.indexOf("%s")+2);
                        }
                        return genFileWriter.getGeneratorViewPath(outputDirTmp, targetFile);
                    }
                });
            }
        }
        // 树结构
        if ((Boolean)map.get("isTreeTable") == true) {
            genFileWriter.write("/templates/generator/vue/tree.vue.ftl",
                    outputDirView, true, "tree.vue", appendPath);
            genFileWriter.write("/templates/generator/vue/tree-table.vue.ftl",
                    outputDirView, true, "tree-table.vue", appendPath);
        }
        // AsideTree结构
        if ((Boolean)map.get("hasAsideTree") == true) {
            genFileWriter.write("/templates/generator/vue/aside-tree.vue.ftl",
                    outputDirView, true, "aside-tree.vue", appendPath);
        }
    }


}
